/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package skat.player;

import java.io.Console;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import rlpw.lang.Log;
import rlpw.util.ForgetfulList;
import rlpw.util.Pair;
import skat.Card;
import skat.Desk;
import skat.Main;
import skat.collections.CardStack;
import skat.collections.Hand;
import skat.event.DeskEvent;
import skat.event.DeskListener;
import skat.games.Game;

/**
 *
 * @author weinpau
 */
public class Player {

    private String name;
    private Hand hand;
    private Memory memory = new Memory();
    private LogicalCards logicalCards = new LogicalCards();
    private boolean ki = true;
    private double risk = 0;

    public Player() {

        Main.desk.addDeskListener(new DeskListener() {

            public void deskAddingCard(DeskEvent e) {
                memory.add(new Pair<Player, Card>(e.getPlayer(), e.getCard()));
                logicalCards.played(e.getPlayer(), e.getCard());
            }
        });

    }

    public double getRisk() {
        return risk;
    }

    public void setRisk(double risk) {
        this.risk = risk;
    }

    public boolean isKI() {
        return ki;
    }

    public void setKI(boolean ki) {
        this.ki = ki;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Hand getHand() {
        return hand;
    }

    public void setHand(Hand hand) {
        this.hand = hand;
    }

    public ForgetfulList<Pair<Player, Card>> getMemory() {
        return memory;
    }

    public List<Card> getPlayable(Card c, Game g) {
        return hand.getFittingCards(c, g);
    }

//    public boolean moveCardToDesk(Card card, Desk desk) {
//        for (Card c : getHand().getFittingCards(desk.isEmpty() ? null : desk.get(0).right(), desk.getGame())) {
//            if (c == card) {
//                desk.add(new Pair<Player, Card>(this, card));
//                getHand().remove(card);
//                return true;
//            }
//        }
//        return false;
//    }
    public void placeCard() {

        Card c = null;

        if (isKI()) {
            c = new KI().getBestCard();
        } else {
            Console console = System.console();
            Log.outln("\n", getHand());
            int i = 0;
            try {
                String index = console.readLine("Index? ");
                i = Integer.parseInt(index);
            } catch (Exception excep) {
            }
            c = getHand().get(i);
        }
        getHand().remove(c);
        Main.desk.add(new Pair<Player, Card>(this, c));

    }

    @Override
    public String toString() {
        return getName();
    }

    class KI {

        private HashMap<Card, CardStatistic> cards = new HashMap<Card, CardStatistic>();

        public KI() {

            CardTree tree = new CardTree();

            for (ArrayList<Card> ls : tree) {
                Desk virtual_desk = new Desk(Main.desk.getGame());
                int i = 0;
                for (Card c : ls) {
                    virtual_desk.add(new Pair<Player, Card>(tree.getPlayers().get(i++), c));
                }
                try {
                    CardStatistic stat = new CardStatistic();
                    Card c = virtual_desk.get(tree.getPlayers().indexOf(Player.this)).right();
                    if (!Main.groups.getGroupOf(virtual_desk.getHighestPair().left()).contains(Player.this)) {
                        stat.setCountNegativ(1);
                        stat.setPointsNegativ(getPoints(ls) + virtual_desk.getPoints());
                    } else {
                        stat.setCountPositiv(1);
                        stat.setPointsPositiv(getPoints(ls) + virtual_desk.getPoints());
                    }

                    if (cards.containsKey(c)) {
                        cards.get(c).add(stat);
                    } else {
                        cards.put(c, stat);
                    }
                } catch (Exception excep) {
                }
            }
        }

        private int getPoints(Card c) {
            List<Card> cl = new ArrayList<Card>();
            for (int j = 0; j < Main.deck.rankCount(); j++) {
                cl.add(new Card(c.suit(), j));
            }
            Collections.sort(cl, Main.comp);
            return Main.deck.rankCount() - cl.indexOf(c);


        }

        private int getPoints(List<Card> ls) {
            int p = 0;
            for (Card c : ls) {
                p += getPoints(c);
            }
            return p;
        }

        public Card getBestCard() {

            CardStatistic stat = new CardStatistic();
            Card card = null;
//           Log.outln("\n", Player.this, ":");
            for (Card c : cards.keySet()) {
//               Log.outln(c, ": ", cards.get(c));
                if (card == null || stat.compareAverageTo(cards.get(c)) == (Main.desk.getGame().isPointsCollect() ? -1 : 1)) {
                    card = c;
                    stat = cards.get(c);
                }
            }

            if (Math.abs(stat.getAverage()) < (1 * (1 - risk))) {
//               Log.outln(card, "-> Risiko");
                for (Card c : cards.keySet()) {
//                   Log.outln(c, ": ", cards.get(c));
                    if (stat.compareNegativTo(cards.get(c)) == (Main.desk.getGame().isPointsCollect() ? 1 : -1)) {
                        card = c;
                        stat = cards.get(c);
                    }
                }
            }

            return card;
        }

        private class CardStatistic {

            private int pointsPositive, pointsNegativ, countPositiv, countNegativ;

            public int getCountNegativ() {
                return countNegativ;
            }

            public void setCountNegativ(int countNegativ) {
                this.countNegativ = countNegativ;
            }

            public int getCountPositiv() {
                return countPositiv;
            }

            public void setCountPositiv(int countPositiv) {
                this.countPositiv = countPositiv;
            }

            public int getPointsPositiv() {
                return pointsPositive;
            }

            public void setPointsPositiv(int points) {
                this.pointsPositive = points;
            }

            public int getPointsNegativ() {
                return pointsNegativ;
            }

            public void setPointsNegativ(int points) {
                this.pointsNegativ = points;
            }

            public void add(CardStatistic cs) {
                setPointsPositiv(cs.getPointsPositiv() + getPointsPositiv());
                setPointsNegativ(cs.getPointsNegativ() + getPointsNegativ());
                setCountPositiv(cs.getCountPositiv() + getCountPositiv());
                setCountNegativ(cs.getCountNegativ() + getCountNegativ());
            }

            public double getAverage() {
                return getAveragePositiv() - getAverageNegativ() * (1.0 - risk);
            }

            private double getAveragePositiv() {

                if (getCountPositiv() == 0) {
                    return 0;
                }
                return (double) getPointsPositiv() / (double) getCountPositiv();
            }

            private double getAverageNegativ() {
                if (getCountNegativ() == 0) {
                    return 0;
                }
                return (double) getPointsNegativ() / (double) getCountNegativ();
            }

            public int compareAverageTo(CardStatistic o) {

                if (getAverage() == o.getAverage()) {
                    return 0;
                }
                if (getAverage() < o.getAverage()) {
                    return -1;
                } else {
                    return 1;
                }
            }

            public int compareNegativTo(CardStatistic o) {
                if (getAverageNegativ() == o.getAverageNegativ()) {
                    return 0;
                }
                if (getAverageNegativ() < o.getAverageNegativ()) {
                    return -1;
                } else {
                    return 1;
                }
            }

            @Override
            public String toString() {
                return "p:" + getAveragePositiv() + ", n: " + getAverageNegativ() + ", a:" + getAverage();
            }
        }

        private class CardTree extends ArrayList<ArrayList<Card>> {

            private List<Player> players = new ArrayList<Player>();

            public CardTree() {

                for (Pair<Player, Card> p : Main.desk) {
                    addCard(p.right());
                    players.add(p.left());
                }

                for (PlayerGroup group : Main.groups) {
                    for (Player p : group) {
                        if (!players.contains(p)) {
                            addPlayer(p);
                            players.add(p);
                        }
                    }

                }

            }

            public List<Player> getPlayers() {
                return players;
            }

            public void addPlayer(Player p) {

                if (isEmpty()) {
                    for (Card c : logicalCards.getLogicalPlayable(p, Main.desk.getFirstCard(), Main.desk.getGame())) {
                        ArrayList<Card> ls = new ArrayList<Card>();
                        ls.add(c);
                        add(ls);

                    }

                } else {
                    int size = size();

                    for (int i = 0; i < size; i++) {

                        ArrayList<Card> ls = get(i);
                        for (Card c : logicalCards.getLogicalPlayable(p, ls.get(0), Main.desk.getGame())) {
                            if (!ls.contains(c)) {
                                ArrayList<Card> nls = new ArrayList<Card>();
                                nls.addAll(ls);
                                nls.add(c);
                                add(nls);
                            }
                        }
                    }
                    for (int i = 0; i < size; i++) {
                        remove(0);
                    }
                }

            }

            public void addCard(Card c) {

                if (isEmpty()) {
                    ArrayList<Card> ls = new ArrayList<Card>();
                    ls.add(c);
                    add(ls);

                } else {
                    for (ArrayList<Card> ls : this) {
                        ls.add(c);
                    }
                }

            }

            @Override
            public String toString() {
                String result = "";
                for (ArrayList<Card> ls : this) {
                    for (Card c : ls) {
                        result += c.toString() + ", ";
                    }
                    result += "\n";

                }
                return result;


            }
        }
    }

    class Memory extends ForgetfulList<Pair<Player, Card>> {

        @Override
        public boolean add(Pair<Player, Card> entry) {

            double rate = getDefaultForgettingRate();

            // Spielentscheidekarten merken sich besser
            List<Card> cl = new ArrayList<Card>();
            for (int i = 0; i < Main.deck.rankCount(); i++) {
                cl.add(new Card(entry.right().suit(), i));
            }
            Collections.sort(cl, Main.comp);
            rate *= .6 + (.4 * (double) (cl.indexOf(entry.right()) + 1) / (double) (Main.deck.rankCount() + 1));

            //Trümpfe merken sich besser
            if (Main.desk.getGame().isTrumpCard(entry.right())) {
                rate *= .5;
            }

            //eigene Karten auch
            if (Player.this == entry.left()) {
                rate *= .2;
            }

            return add(entry, rate);
        }
    }

    class LogicalCards {

        private HashMap<Player, ArrayList<Card>> logicals = new HashMap<Player, ArrayList<Card>>();

        private void init(Player player) {
            logicals.put(player, new CardStack(Main.deck).draw(32));

        }

        public List<Card> getLogicalPlayable(Player player, Card card, Game game) {

            Hand testhand = new Hand();
            for (Card c : getSureLogicalCards(player)) {
                testhand.add(c);
            }

            if (!testhand.getFollowSuitCards(card, game).isEmpty()) {

                Hand hand = new Hand();
                for (Card c : getAllLogicalCards(player)) {
                    hand.add(c);
                }
                return hand.getFittingCards(card, game);

            }


            return getAllLogicalCards(player);

        }

        private List<Card> getSureLogicalCards(Player player) {

            List<Card> sure_ls = getAllLogicalCards(player);

            if ((player == Player.this)) {
                return sure_ls;
            }

            for (Player p : logicals.keySet()) {
                if (player != p) {
                    for (Card c : getAllLogicalCards(p)) {
                        if (sure_ls.contains(c)) {
                            sure_ls.remove(c);
                        }
                    }
                }
            }

            return sure_ls;

        }

        @SuppressWarnings("unchecked")
		public List<Card> getAllLogicalCards(Player player) {

            if (!logicals.containsKey(player)) {
                init(player);
            }
            if ((player == Player.this)) {
                return player.getHand();
            }

            ArrayList<Card> cards = (ArrayList<Card>) logicals.get(player).clone();
            for (Card card : (ArrayList<Card>) cards.clone()) {
                for (int i = 0; i < memory.size(); i++) {
                    Card c = memory.get(i).right();
                    cards.remove(c);

                }
                // eigene Karten haben andere nicht
                for (Card c : getHand()) {
                    if (c.equals(card)) {
                        cards.remove(c);
                    }

                }
            }

            return cards;


        }

        @SuppressWarnings("unchecked")
		public void played(Player player, Card card) {

            if (!logicals.containsKey(player)) {
                init(player);
            }

            // Schlüsse aus nicht Bedienen ziehen ...
            Hand h = (Hand) player.getHand().clone();
            h.add(card);
            if (h.getFollowSuitCards(Main.desk.getFirstCard(), Main.desk.getGame()).isEmpty()) {

                if (Main.desk.getGame().isTrumpCard(Main.desk.getFirstCard())) {
                    // hat kein Trumpf mehr
                    for (Card c : (List<Card>) logicals.get(player).clone()) {
                        if (Main.desk.getGame().isTrumpCard(c)) {
                            logicals.get(player).remove(c);
                        }
                    }

                } else {
                    // hat die Farbe nicht mehr
                    for (Card c : (List<Card>) logicals.get(player).clone()) {
                        if (Main.desk.getFirstCard().suit() == c.suit()) {
                            logicals.get(player).remove(c);
                        }

                    }

                }
            }

        }
    }
}
